package br.ifrn.tads.poo.negocio;
import br.ifrn.tads.poo.pessoa.*;
import br.ifrn.tads.poo.excecao.*;

import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.ArrayList;
import java.text.SimpleDateFormat;
import java.util.Date;

import br.ifrn.tads.poo.carro.Carro;

public class Locadora implements GerenciaDeSistema, GerenciaDeClientes, GerenciaDeCarros {
	
	public Set <Carro> carros = new HashSet <Carro>();
	public Set <Reserva> reservas = new HashSet <Reserva>();
	public Set <Aluguel> alugueis = new HashSet <Aluguel>();
	public Set <Cliente> clientes = new HashSet <Cliente>();
	public Set <Funcionario> funcionarios = new HashSet <Funcionario>();
	
	/*\
	 * 	Implementações da Interface GerenciaDeCarros
	\*/
	
	public void validarPlaca(String placa) throws PlacaExistenteException, PlacaFormatoInvalidoException {
		String regexPlaca = "[A-Z]{3,3}-\\d{4,4}";
		if (!placa.matches(regexPlaca)) {
			throw new PlacaFormatoInvalidoException("\n[!] Placa em formato inválido (ABC-1234)");
		}
		
		for (Iterator iterator = carros.iterator(); iterator.hasNext();) {
			Carro carro = (Carro) iterator.next();
			if (carro.getPlaca().equalsIgnoreCase(placa)) {
				throw new PlacaExistenteException("\n[!] Placa já existe no sistema");
			}
		}
	}
	
	public void adicionar(Carro carro) {
		this.carros.add(carro);
	}
	
	public String carroResumo(Carro carro) {
		return "* " + carro.getPlaca() + " | " + carro.getModelo() + " | " + carro.getCor() + " | Diária: R$ " + carro.getValorDiaria() + ",00";
	}
	
	public Carro getCarro(String modelo, String cor) {
		for (Iterator iterator = carros.iterator(); iterator.hasNext();) {
			Carro carro = (Carro) iterator.next();
			if (disponivel(carro)) {
				if (carro.getModelo().equalsIgnoreCase(modelo) && carro.getCor().equalsIgnoreCase(cor)) {
					return carro;
				}
			}
		}		
		return null;
	}
	
	public Carro getCarro(String placa) throws PlacaInexistenteException {
		for (Iterator iterator = carros.iterator(); iterator.hasNext();) {
			Carro carro = (Carro) iterator.next();
			if (carro.getPlaca().equalsIgnoreCase(placa)) {
				return carro;
			}
		}
		throw new PlacaInexistenteException("\n[!] Carro não encontrado");
	}
	
	public ArrayList listarCarros() {
		ArrayList<Carro> lista = new ArrayList<Carro>();
		
		for (Iterator iterator = carros.iterator(); iterator.hasNext();) {
			Carro carro = (Carro) iterator.next();
			lista.add(carro);
		}		
		
		if (lista.isEmpty()) {
			return null;
		}
		
		return lista;
	}
	
	/*\
	 * 	Implementação dos métodos próprios da Locadora
	\*/
	
	public ArrayList listarDisponiveis() {
		ArrayList<Carro> lista = new ArrayList<Carro>();
		
		for (Iterator iterator = carros.iterator(); iterator.hasNext();) {
			Carro carro = (Carro) iterator.next();
			if (disponivel(carro)) {
				lista.add(carro);
			}
		}		
		
		if (lista.isEmpty()) {
			return null;
		}
		
		return lista;
	}
	
	public ArrayList listarAlugados() {
		ArrayList<Carro> lista = new ArrayList<Carro>();
		
		for (Iterator iterator = carros.iterator(); iterator.hasNext();) {
			Carro carro = (Carro) iterator.next();
			if (alugado(carro)) {
				lista.add(carro);
			}
		}		
		
		if (lista.isEmpty()) {
			return null;
		}
		
		return lista;
	}
	
	public ArrayList listarReservados() {
		ArrayList<Carro> lista = new ArrayList<Carro>();
		
		for (Iterator iterator = carros.iterator(); iterator.hasNext();) {
			Carro carro = (Carro) iterator.next();
			if (reservado(carro)) {
				lista.add(carro);
			}
		}		
		
		if (lista.isEmpty()) {
			return null;
		}
		
		return lista;
	}
	
	public boolean reservado(Carro carro) {		
		for (Iterator iterator = reservas.iterator(); iterator.hasNext();) {
			Reserva reserva = (Reserva) iterator.next();
			if (reserva.getCarro() == carro) {
				return true;
			}
		}
		return false;
	}
	
	public boolean alugado(Carro carro) {
		for (Iterator iterator = alugueis.iterator(); iterator.hasNext();) {
			Aluguel aluguel = (Aluguel) iterator.next();		
			if (aluguel.getCarro() == carro) {
				return true;
			}
		}
		return false;
	}
	
	public boolean disponivel(Carro carro) {
		if(alugado(carro) || reservado(carro)){
			return false;
		}
		return true;
	}
	
	public void alugar(Carro carro, Cliente cliente) {
		Aluguel aluguel = new Aluguel(cliente, carro);
		this.alugueis.add(aluguel);
	}
	
	public void alugar(Reserva reserva) {
		Aluguel aluguel = new Aluguel(reserva);
		this.alugueis.add(aluguel);
	}
	
	public void alugar(Carro carro, Cliente cliente, Date data) {
		Aluguel aluguel = new Aluguel(cliente, carro, data);
		this.alugueis.add(aluguel);
	}
	
	public Aluguel getAluguel(Cliente cliente) {
		for (Iterator iterator = alugueis.iterator(); iterator.hasNext();) {
			Aluguel aluguel = (Aluguel) iterator.next();
			if (aluguel.getCliente() == cliente) {
				return aluguel;
			}
		}
		return null;
	}
	
	public Aluguel getAluguel(Carro carro) {
		for (Iterator iterator = alugueis.iterator(); iterator.hasNext();) {
			Aluguel aluguel = (Aluguel) iterator.next();
			if (aluguel.getCarro() == carro) {
				return aluguel;
			}
		}
		return null;
	}
	
	public ArrayList listarAlugueis() {
		ArrayList<Aluguel> lista = new ArrayList<Aluguel>();
		
		for (Iterator iterator = alugueis.iterator(); iterator.hasNext();) {
			Aluguel aluguel = (Aluguel) iterator.next();
			lista.add(aluguel);
		}		
		
		if (lista.isEmpty()) {
			return null;
		}
		
		return lista;
	}
	
	public String aluguelResumo(Aluguel aluguel) {
		SimpleDateFormat formatoData = new SimpleDateFormat("dd/MM/yyyy");
		
		aluguel.setDataDevolucao(new Date());
		aluguel.calcular();
		return	"CLIENTE\n" + clienteResumo(aluguel.getCliente()) + 
				"\nCARRO\n" + carroResumo(aluguel.getCarro()) +
				"\nRETIRADA\n" + "* " + formatoData.format(aluguel.getDataRetirada()) +
				"\nVALOR ATUAL\n" + "* R$ " + aluguel.getValor() + ",00";
	}
	
	public int devolver(Aluguel aluguel) {
		aluguel.setDataDevolucao(new Date());
		aluguel.calcular();
		int valor = aluguel.getValor();
		this.alugueis.remove(aluguel);
		return valor;
	}
	
	public void validarDataRetirada(Date data) throws DataExcedidaException {
		Date dataAtual = new Date();
		long diff = ((data.getTime() - dataAtual.getTime()) / (60 * 60 * 1000)) / 24;
		
		if (diff < 0) {
			throw new DataExcedidaException("\n[!] Data de retirada inválida");
		}
				
		if (diff > 13) { // o cálculo da diff tá dando um desconto de 2 dias aproximadamente
			throw new DataExcedidaException("\n[!] Data de retirada excedida. O intervalo não pode ser maior que 15 dias contando da data atual");
		}
	}
	
	public void reservar(Carro carro, Cliente cliente, Date previsaoRetirada) {
		Reserva reserva = new Reserva(cliente, carro, previsaoRetirada);
		this.reservas.add(reserva);
	}
	
	public Reserva getReserva(Cliente cliente) {
		for (Iterator iterator = reservas.iterator(); iterator.hasNext();) {
			Reserva reserva = (Reserva) iterator.next();
			if (reserva.getCliente() == cliente) {
				return reserva;
			}
		}
		return null;
	}
	
	public Reserva getReserva(Carro carro) {
		for (Iterator iterator = reservas.iterator(); iterator.hasNext();) {
			Reserva reserva = (Reserva) iterator.next();
			if (reserva.getCarro() == carro) {
				return reserva;
			}
		}
		return null;
	}
	
	public ArrayList listarReservas() {
		ArrayList<Reserva> lista = new ArrayList<Reserva>();
		
		for (Iterator iterator = reservas.iterator(); iterator.hasNext();) {
			Reserva reserva = (Reserva) iterator.next();
			lista.add(reserva);
		}		
		
		if (lista.isEmpty()) {
			return null;
		}
		
		return lista;
	}
	
	public ArrayList listarReservasParaHoje() {
		ArrayList<Reserva> lista = new ArrayList<Reserva>();
		
		SimpleDateFormat formatoData = new SimpleDateFormat("dd/MM/yyyy");
		String stringDataAtual = formatoData.format(new Date());
		
		for (Iterator iterator = reservas.iterator(); iterator.hasNext();) {
			Reserva reserva = (Reserva) iterator.next();
			String stringDataRetirada = formatoData.format(reserva.getDataRetirada());
			if (stringDataAtual.equalsIgnoreCase(stringDataRetirada)) {
				lista.add(reserva);
			}
		}		
		
		if (lista.isEmpty()) {
			return null;
		}
		
		return lista;
	}
	
	public void cancelarReserva(Reserva reserva) {
		this.reservas.remove(reserva);
	}
	
	public String reservaResumo(Reserva reserva) {
		SimpleDateFormat formatoData = new SimpleDateFormat("dd/MM/yyyy");
		return	"CLIENTE\n" + clienteResumo(reserva.getCliente()) + 
				"\nCARRO\n" + carroResumo(reserva.getCarro()) +
				"\nPREVISÃO DE RETIRADA\n" + "* " + formatoData.format(reserva.getDataRetirada());
	}
	
	/*\
	 * 	Implementações da Interface GerenciaDeSistema
	\*/
	
	public void cadastrarFuncionario(Funcionario funcionario, String login, String senha) {
		this.funcionarios.add(funcionario);
		funcionario.setUsuario(login);
		funcionario.setSenha(senha);
	}
	
	public ArrayList listarFuncionarios() {
		ArrayList<Funcionario> lista = new ArrayList<Funcionario>();
		
		for (Iterator iterator = funcionarios.iterator(); iterator.hasNext();) {
			Funcionario funcionario = (Funcionario) iterator.next();
			lista.add(funcionario);
		}		
		
		if (lista.isEmpty()) {
			return null;
		}
		
		return lista;
	}
	
	public String funcionarioResumo(Funcionario funcionario) {
		return "* " + funcionario.getNome() + " | " + funcionario.getUsuario();
	} 
	
	public Funcionario verificarLogin(String login) {
		for (Iterator iterator = funcionarios.iterator(); iterator.hasNext();) {
			Funcionario funcionario = (Funcionario) iterator.next();
			if (funcionario.getUsuario().equalsIgnoreCase(login)) {
				return funcionario;
			}
		}
		return null;
	}
	
	public boolean verificarSenha(Funcionario funcionario, String senha) {
		if (funcionario.getSenha().equalsIgnoreCase(senha)) {
			return true;
		}
		return false;
	}
	
	/*\
	 * 	Implementações da Interface GerenciaDeClientes
	\*/
	
	public void validarCpf(String cpf) throws CpfInvalidoException {
		String regexCpf = "\\d{3}.\\d{3}.\\d{3}-\\d{2}";
		if (!cpf.matches(regexCpf)) {
			throw new CpfInvalidoException("\n[!] CPF inválido (xxx.xxx.xxx-xx)");
		}
	}
	
	public void validarCnpj(String cnpj) throws CnpjInvalidoException {
		String regexCnpj = "\\d{2}.\\d{3}.\\d{3}/\\d{4}-\\d{2}";
		if (!cnpj.matches(regexCnpj)) {
			throw new CnpjInvalidoException("\n[!] CNPJ inválido (xx.xxx.xxx/xxxx-xx)");
		}
	}
	
	public void validarIdadeCliente(int idade) throws IdadeNaoPermitidaException {
		if (idade < 18) {
			throw new IdadeNaoPermitidaException("\n[!] O cliente deve ser maior de idade (18 anos)");
		}
	}
	
	public void cadastrarCliente(ClientePessoaJuridica cliente) {
		this.clientes.add(cliente);		
	}
	
	public void cadastrarCliente(ClientePessoaFisica cliente) {
		this.clientes.add(cliente);
	}
	
	public Cliente getCliente(String string) {
		//	O laço irá percorrer todos os clientes da coleção
		
		for (Iterator iterator = clientes.iterator(); iterator.hasNext();) {
			Cliente cliente = (Cliente) iterator.next();			
			
			//	Se o nome bater, retorna o cliente
			
			if (string.equalsIgnoreCase(cliente.getNome())) {
				return cliente;
			}
			
			//	Se o cliente for do tipo PessoaJuridica, faço o casting em cliente, e confiro os campos Cnpj e Responsavel
			
			if (cliente.getClass() == ClientePessoaJuridica.class){
				ClientePessoaJuridica pessoaJuridica = (ClientePessoaJuridica) cliente;
				if (string.equalsIgnoreCase(pessoaJuridica.getCnpj()) || string.equalsIgnoreCase(pessoaJuridica.getResponsavel())) {
					return pessoaJuridica;
				}	
			}
				
			//	Se o cliente for do tipo PessoaFisica, faço o casting em cliente, e confiro os campos Cpf e Rg	 
			
			if (cliente.getClass() == ClientePessoaFisica.class){
				ClientePessoaFisica pessoaFisica = (ClientePessoaFisica) cliente;
				if (string.equalsIgnoreCase(pessoaFisica.getCpf()) || string.equalsIgnoreCase(pessoaFisica.getIdentidade())) {
					return pessoaFisica;
				}
			}
			
		} 
		
		return null;
	}
	
	public ArrayList listarClientes() {
		ArrayList<Cliente> lista = new ArrayList<Cliente>();
		
		for (Iterator iterator = clientes.iterator(); iterator.hasNext();) {
			Cliente cliente = (Cliente) iterator.next();
			lista.add(cliente);
		}		
		
		if (lista.isEmpty()) {
			return null;
		}
		
		return lista;
	}
	
	public String clienteResumo(Cliente cliente) {
		
		if (cliente.getClass() == ClientePessoaJuridica.class) {
			ClientePessoaJuridica pessoaJuridica = (ClientePessoaJuridica) cliente;
			return "* " + "CNPJ: " + pessoaJuridica.getCnpj() + " | " + pessoaJuridica.getNome();
		} else if (cliente.getClass() == ClientePessoaFisica.class) {
			ClientePessoaFisica pessoaFisica = (ClientePessoaFisica) cliente;
			return "* " + "CPF: " + pessoaFisica.getCpf() + " | " + pessoaFisica.getNome();
		}
		
		return null;
	}
}
